/*****************************************************************************************************
*       SRLOS Team
*@filename:     kernel_task_loader.c
*@brief   :     load excutable files to memory used to create process
*@author  :     bloceanc
*@note    :
*@comment :     there are some special functions in this files, pay attention to these!
*@datetime:      27/04/2010 19:54:18
*****************************************************************************************************/
#ifndef _K_TASK_LOADER_C_
#define _K_TASK_LOADER_C_

#include "kernel_task_def.h"
#include "../mem/k_mem_physical.h"
#include "../mem/k_mem_sysmmu.h"
#include "../k_common_asm.h"

extern void *k_mm_usr_heap_allocate(unsigned int size);
extern void *k_mm_usr_heap_free(void *mem_addr);

/**
 * @BRIEF	: Allocate code segment memorys
 * @PARAM   : page_count        code segment memory page count
 * @RETRUN	: loaded address(virtual address), if 0, failed
 */
static unsigned int *k_task_allocate_codesegment(unsigned int page_count)
{
    // compute slpt count
    unsigned int *p_cur_flpt = (unsigned int *)(K_MEM_MMU_SYS_PHY2VIR(g_p_cur_process->fsttb_phy_addr));
    unsigned int i_slpt_count = (page_count + 1023) & ~1023;
    unsigned int i_flpt_rcd_count = i_slpt_count >> 2;
    unsigned int *p_slpt_array;
    unsigned int *p_page_array;
    unsigned int i,j;

    // allocate all slpt. we must use user heap. because , we load codes to current process user space!
    p_slpt_array = k_mm_usr_heap_allocate(i_slpt_count * sizeof(unsigned int*));

    if(p_slpt_array == 0)
    {
        // failed
        return 0;
    }

    p_page_array = k_mm_usr_heap_allocate(page_count * sizeof(unsigned int *));

    if(p_page_array == 0)
    {
        k_mm_usr_heap_free(p_slpt_array);
        return 0;
    }

    // now allocate SLPTs
    for(i = 0; i < i_slpt_count; i++)
    {
        unsigned int * p_tmp_slpt = k_mem_mmu_allocate(K_MEM_PHY_SYSMOD, 1);
        if(p_tmp_slpt == 0)
        {
            unsigned int j;
            for(j = 0; j < i; j++)
            {
                k_mem_mmu_free((unsigned int *)p_slpt_array[j]);
            }
            k_mm_usr_heap_free(p_page_array);
            k_mm_usr_heap_free(p_slpt_array);
            // allocate SLPT failed
            return 0;
        }
        p_slpt_array[i] = (unsigned int)p_tmp_slpt;
    }

    // allocate physcial page
    for(i=0; i < page_count; i++)
    {
        unsigned int *p_phy_tmp;
        // we have to use physical allocate, if mmu, we can't find which SLPT has filled!
        // so, we should control interrupt
        // disable it;
        K_MEM_DISABLE_INTERRUPT;
        p_phy_tmp = k_mem_phy_allocate(K_MEM_PHY_NORMAL,1);
        K_MEM_ENABLE_INTERRUPT;
        if(p_phy_tmp == 0)
        {
            for(j = 0; j < i; j++)
            {
                // free alloceated
                k_mem_phy_free((unsigned int)p_phy_tmp);
            }

            break;
        }

        if(i != page_count)
        {
            // failed allocate physical page
            for(j = 0; j < i_slpt_count; j++)
            {
                k_mem_mmu_free((unsigned int *)p_slpt_array[j]);
            }
            k_mm_usr_heap_free(p_page_array);
            k_mm_usr_heap_free(p_slpt_array);
            return 0;
        }

        p_page_array[i] = (unsigned int)p_phy_tmp;
    }

    // now we have enough SLPT
    // find unused coutinuous FLPT records in usr heap space!
    for(i = K_SYS_PROCESS_HEAPSTACKSTART_START; i < K_SYS_PROCESS_HEAPSTACKSTART_END; i+=4)
    {
        unsigned int i_tmp = i + i_flpt_rcd_count;
        // disable interrupt
        K_MEM_DISABLE_INTERRUPT;
        for(j = i; j < i_tmp; j++)
        {
            if(p_cur_flpt[j] & 0x01)
            {
                // shit...we can't use currents
                // we fill FLPT alway use 4 rcds once time.. so, jump 4 rcds;
                K_MEM_ENABLE_INTERRUPT;
                break;
            }
        }

        if(j == i_tmp)
        {
            // yeah, we found them
            // fill them
            unsigned int cur_addr;
            cur_addr = i << 20;

            // first we need fill the FLPT then SLPT!
            for(j = 0; j < i_slpt_count; j++)
            {
                unsigned int k;
                for(k = 0; k < 4; k++)
                {
                    K_MEM_MMU_FILL_FMPT(((unsigned int)p_slpt_array[j] + (k << 10)), (i << 20), K_MEM_MMU_DOMAIN_SYSTEM, (unsigned int)p_cur_flpt)
                    i++;
                }
            }

            // fill the SLPT
            for(j = 0; j < page_count; j++)
            {
                K_MEM_MMU_FILL_SMPT((unsigned int)p_page_array[j], (cur_addr + (j << 12)), p_cur_flpt, K_MEM_MMU_DOMAIN_USER, K_MEM_MMU_WRITE_MODE_WB,K_MEM_MMU_WRITE_MODE_B)
            }

            // enable interrupt
            K_MEM_ENABLE_INTERRUPT;

            return (unsigned int *)cur_addr;
        }
        else
        {
            K_MEM_ENABLE_INTERRUPT;
            // shit.. we can't find continuous free page... failed. we can't do any more..!
            for(j = 0; j < i_slpt_count; j++)
            {
                k_mem_phy_free(p_slpt_array[j]);
            }

            for(j= 0; j < page_count; j++)
            {
                k_mem_phy_free(p_page_array[j]);
            }

            k_mm_usr_heap_free(p_slpt_array);
            k_mm_usr_heap_free(p_page_array);

            return 0;
        }
    }

	return 0;
}


/**
 * @BRIEF	: parse and load excutable file to memory
 * @PARAM	: dir               the excutable file path
 * @PARAM	: load_flag         indicate whether load from ROM
 * @PARAM   : size              loaded memory size
 * @RETRUN	: loaded address
 * @WARNING	:
 * @NOTE    : if load_flag is > 0, load file from ROM.Dir indicates the ROM address.And load_flag is
 *          : the size of codes in ROM needed to be load.
 *          : load_flag sized by BYTES
 */
unsigned int *k_task_load(char *dir, int load_flag, unsigned int *size)
{
    // allocate special space to save codesegment
    if(load_flag)
    {
        // load from ROM, dir is the address in ROM

        // we just move datas
        unsigned int i;
        unsigned int count = load_flag >> 2;
        unsigned int *mem_addr;
        unsigned int *psrc = (unsigned int *)dir;
        unsigned int *pdst;

        if((unsigned int)dir & 0xf)
        {
            // if being address, must be bound of 4 bytes!
            return 0;
        }

		// allocate space from usr space..this is very speical!
		mem_addr = k_task_allocate_codesegment((load_flag + (1 << 12 - 1) )>> 12);
        if(mem_addr == 0)
        {
            // failed
            return 0;
        }

        pdst = (unsigned int *)mem_addr;

        for(i = 0; i < count; i++)
        {
            *(pdst + i)=*(psrc + i);
        }
        *size = load_flag;
        return mem_addr;
    }
    else
    {
        // need to parse excutable file format,then load to memory
        // first allocate special memory, then just load to it!
        unsigned int *mem_addr = k_task_allocate_codesegment((load_flag + (1 << 12 - 1) )>> 12);



        if(mem_addr == 0)
        {
            return 0;
        }

		return mem_addr;
    }

    
}

/**
 * @BRIEF	: Swap code segment to new process virtual space!
 * @PARAM   : p_newflpt         flpt of new process
 * @PARAM   : addr_codesegment  codesegment address of new process(this configured in basedefine.h)
 * @PARAM   : code_addr         new codesegment address of current process
 * @PARAM   : seg_size          codesegment size in bytes!
 * @RETRUN	: void
 * @NOTE    : This function use swap current process with new process!
 */
void k_task_codesegment_swap(unsigned int *p_newflpt, unsigned int addr_codesegment, unsigned int *code_addr, unsigned int seg_size)
{

    if(p_newflpt == 0 || seg_size == 0)
    {
        return;
    }
    else
    {
        unsigned int rcd_count_flpt = (seg_size + ((1 << 20) - 1)) & ~((1 << 20) - 1);  // be care of this
        unsigned int *p_cur_flpt = (unsigned int *)(K_MEM_MMU_SYS_PHY2VIR(g_p_cur_process->fsttb_phy_addr));
        unsigned int idx_cur_code = ((unsigned int)code_addr) >> 20;                                // code segment FLPT index in current process
        unsigned int i;

        for(i = 0; i < rcd_count_flpt; i++,idx_cur_code++)
        {
            p_newflpt[idx_cur_code] = p_cur_flpt[idx_cur_code];
            // clear father process FLPT record
            p_cur_flpt[idx_cur_code] = 0;
        }
    }
}

#endif /* _K_TASK_LOADER_C_ */
